package conf2;

import conf2.iface.IGamePieceListener;
import conf2.iface.MoveListener;
import conf2.model.EActor;
import conf2.model.ESpecialCard;
import conf2.model.GameState;
import conf2.model.Move;
import conf2.model.WorldState;
import conf2.net.IConnection;
import conf2.net.Message;
import conf2.text.Cards;
import conf2.ui.GamePiece;
import conf2.ui.MainMenu;
import conf2.ui.MainWindow;
import conf2.ui.PieceCollection;
import conf2.ui.Positions;
import conf2.ui.SpecialCardMiniature;
import conf2.ui.SpecialCardWidget;
import java.awt.Point;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

class Game implements Runnable, SpecialCardWidget.Listener, MoveListener
{

    private final MainWindow mw;
    private final MainMenu menu;
    private final IConnection conn;
    private final Configuration config;
    private final GameState gs;
    private final WorldState ws;
    private final PieceCollection pieceColl;
    private boolean isRunning = true;
    private Map<ESpecialCard, SpecialCardWidget> specialCardWidgets =
            new EnumMap<ESpecialCard, SpecialCardWidget>(ESpecialCard.class);
    private Set<ESpecialCard> playedSpecialCards = new HashSet<ESpecialCard>();
    private Map<ESpecialCard, SpecialCardMiniature> specialCardMiniatures =
            new EnumMap<ESpecialCard, SpecialCardMiniature>(ESpecialCard.class);
    private final IGamePieceListener pieceHandler;
    private final Animator animator;

    private enum State
    {

        IDLE,
        WAITING_FOR_OPPONENT_MOVE,
        MY_MOVE,
    }
    private State state = State.IDLE;

    public Game(MainWindow mw,
            MainMenu menu,
            IConnection conn,
            Configuration config,
            GameState gs,
            WorldState ws,
            PieceCollection pieceColl)
    {
        this.mw = mw;
        this.menu = menu;
        this.conn = conn;
        this.config = config;
        this.gs = gs;
        this.ws = ws;
        this.pieceColl = pieceColl;

        animator = new Animator(pieceColl);

        pieceHandler = new PieceHandler(mw, config, ws, gs, pieceColl, this);
        for (GamePiece gp : pieceColl) {
            gp.addGamePieceListener(pieceHandler);
        }

        if (gs.isMyTurn()) {
            state = State.MY_MOVE;
        } else {
            state = State.WAITING_FOR_OPPONENT_MOVE;
        }

        createSpecialCardWidgets();
    }

    public void moveMade(Move move)
    {
        conn.sendMessage(new Message(Message.Type.MOVE, move));

        ws.placeActor(move.getAttacker(), move.getTo());
        // TODO: Animate
        PiecePositioner.orderIn(move.getFrom(), pieceColl, ws);
        PiecePositioner.orderIn(move.getTo(), pieceColl, ws);

        System.out.println("MOVE MADE: " + move);

        gs.nextTurn();

        state = State.WAITING_FOR_OPPONENT_MOVE;
    }

    public void attackMoveMade(Move move)
    {
        System.out.println("ATTACK MOVE MADE: " + move);
    }

    public void run()
    {
        while (isRunning) {

            switch (state) {
                case WAITING_FOR_OPPONENT_MOVE:
                    setMySideMovable(false);
                    mw.showCenteredWaitMessage("Waiting for opponent to move...");
                    AsyncWaiter<Move> moveWaiter = new AsyncWaiter<Move>(conn, Move.class);
                    moveWaiter.addListener(new AsyncWaiter.Listener<Move>()
                    {

                        public void completed(Move result)
                        {
                            mw.hideCenteredWaitMessage();
                            gotOpponentMove(result);
                        }
                    });
                    moveWaiter.execute();
                    state = State.IDLE;
                    break;
                case MY_MOVE:
                    checkSpecialCardsAvailability();
                    setMySideMovable(true);
                    state = State.IDLE;
                    break;
                case IDLE:

                    try {
                        Thread.sleep(250);
                    } catch (Exception ignored) {
                    }
                    break;
                default:
                    System.out.println("WARNING: Unhandled state: " + state + "! Setting to IDLE");
                    state = State.IDLE;
            }


        }

        menu.setGameAsClientEnabled(true);
        menu.setGameAsServerEnabled(true);
    }

    private void checkSpecialCardsAvailability()
    {
        for (ESpecialCard c : gs.getMySpecialCards()) {
            if (playedSpecialCards.contains(c)) {
                specialCardWidgets.get(c).setEnabled(false);
            } else {
                specialCardWidgets.get(c).setEnabled(ws.canPlaySpecialCard(c));
            }
        }
    }

    private void createSpecialCardWidgets()
    {
        final int panelWidth = mw.getMainPanelSize().width;
        final int panelHeight = mw.getMainPanelSize().height;
        Point[] cardPos = Positions.createMySpecialCardWidgetLocations(panelWidth, panelHeight);
        Point[] oppCardPos = Positions.createOpponentSpecialCardWidgetLocations(panelWidth);

        int i = 0;
        for (ESpecialCard c : gs.getMySpecialCards()) {
            SpecialCardWidget scw = new SpecialCardWidget(c,
                    "<html><b>" + Cards.getSpecialCardName(c) + "</b><br>" + Cards.getSpecialCardDescription(c) + "</html>",
                    Cards.DISABLED_PLAYER_SPECIAL_CARD_TOOLTIP,
                    this);

            scw.setLocation(cardPos[i]);
            specialCardWidgets.put(c, scw);
            mw.addSpecialCardWidget(scw);
            i++;
        }

        i = 0;
        for (ESpecialCard c : gs.getOpponentSpecialCards()) {
            SpecialCardMiniature scm = new SpecialCardMiniature(c);
            specialCardMiniatures.put(c, scm);
            scm.setLocation(oppCardPos[i]);
            mw.addSpecialCardMiniature(scm);
            i++;
        }


    }



    private void setMySideMovable(boolean b)
    {
        for (GamePiece gp : pieceColl) {
            EActor actor = gp.getActor();
            if (actor.getSide() == gs.getMySide()) {
                gp.setMovable(b);
            }
        }
    }

    public void specialCardClicked(ESpecialCard which)
    {
        System.out.println("Clicked " + which);
    }

    private void gotOpponentMove(final Move move)
    {
        System.out.println("Got move: " + move);
        if (move.getDefender() == null) {
            // Non-attacking move.

            animator.queueAnimation(move, new Animation.Listener()
            {

                public void animationFinished()
                {
                    ws.placeActor(move.getAttacker(), move.getTo());
                    PiecePositioner.orderIn(move.getFrom(), pieceColl, ws);
                    PiecePositioner.orderIn(move.getTo(), pieceColl, ws);
                    state = State.MY_MOVE;
                }
            });
            animator.start();


        } else {
            // Attack move.
        }

    }
}
